import os
import chardet
import codecs
from pathlib import Path
import logging

# 配置日志
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(levelname)s - %(message)s'
)

# 常见的中文编码列表
COMMON_ENCODINGS = ['utf-8', 'gb18030', 'gbk', 'big5', 'utf-16']

# 编码映射关系
ENCODING_MAPPING = {
    'gb2312': 'gb18030',  # GB2312 映射到 GB18030
    'gbk': 'gb18030',     # GBK 也映射到 GB18030
}

def normalize_encoding(encoding):
    """标准化编码名称"""
    if not encoding:
        return None
    encoding = encoding.lower()
    return ENCODING_MAPPING.get(encoding, encoding)

def detect_encoding(file_path):
    """检测文件编码"""
    try:
        with open(file_path, 'rb') as f:
            raw_data = f.read()
            if not raw_data:
                logging.warning(f"文件 {file_path} 为空")
                return None
            
            # 首先尝试使用chardet检测
            result = chardet.detect(raw_data)
            detected_encoding = result['encoding']
            confidence = result['confidence']
            
            # 标准化检测到的编码
            detected_encoding = normalize_encoding(detected_encoding)
            
            # 如果检测结果可信度较高，直接返回
            if detected_encoding and confidence > 0.7:
                logging.info(f"文件 {file_path} 检测到的编码: {detected_encoding} (置信度: {confidence})")
                return detected_encoding
            
            # 如果检测结果不可靠，尝试常见编码
            logging.info(f"文件 {file_path} 编码检测结果不可靠，尝试常见编码")
            for encoding in COMMON_ENCODINGS:
                try:
                    raw_data.decode(encoding)
                    logging.info(f"文件 {file_path} 成功使用 {encoding} 解码")
                    return encoding
                except UnicodeDecodeError:
                    continue
            
            logging.warning(f"文件 {file_path} 无法确定编码")
            return None
    except Exception as e:
        logging.error(f"检测文件 {file_path} 编码时出错: {str(e)}")
        return None

def try_read_file(file_path, encoding):
    """尝试使用指定编码读取文件"""
    try:
        with codecs.open(file_path, 'r', encoding=encoding) as f:
            return f.read()
    except UnicodeDecodeError:
        return None

def convert_file_encoding(file_path, target_encoding):
    """转换单个文件的编码"""
    try:
        # 检测当前文件编码
        current_encoding = detect_encoding(file_path)
        if not current_encoding:
            logging.error(f"无法检测文件 {file_path} 的编码，跳过转换")
            return
        
        # 如果当前编码与目标编码相同，则跳过
        if current_encoding.lower() == target_encoding.lower():
            logging.info(f"文件 {file_path} 已经是 {target_encoding} 编码，跳过转换")
            return
        
        # 尝试读取文件内容
        content = try_read_file(file_path, current_encoding)
        if content is None:
            # 如果使用检测到的编码无法读取，尝试其他常见编码
            logging.warning(f"使用检测到的编码 {current_encoding} 无法读取文件，尝试其他编码")
            for encoding in COMMON_ENCODINGS:
                if encoding.lower() == current_encoding.lower():
                    continue
                content = try_read_file(file_path, encoding)
                if content is not None:
                    current_encoding = encoding
                    logging.info(f"成功使用 {encoding} 读取文件")
                    break
            
            if content is None:
                logging.error(f"无法读取文件 {file_path}，跳过转换")
                return
        
        # 生成新文件名
        file_path = Path(file_path)
        new_filename = f"{file_path.stem}_{target_encoding}{file_path.suffix}"
        new_file_path = file_path.parent / new_filename
        
        # 保存为新编码
        try:
            with codecs.open(new_file_path, 'w', encoding=target_encoding) as f:
                f.write(content)
            logging.info(f"已将文件 {file_path} 转换为 {target_encoding} 编码，保存为 {new_filename}")
        except Exception as e:
            logging.error(f"保存文件 {new_file_path} 时出错: {str(e)}")
    except Exception as e:
        logging.error(f"处理文件 {file_path} 时发生未知错误: {str(e)}")

def batch_convert_encoding(directory, target_encoding):
    """批量转换目录下所有txt文件的编码"""
    try:
        directory = Path(directory)
        if not directory.exists():
            logging.error(f"目录 {directory} 不存在")
            return
        
        # 遍历目录下的所有txt文件
        txt_files = list(directory.glob('**/*.txt'))
        if not txt_files:
            logging.warning(f"在目录 {directory} 中没有找到txt文件")
            return
        
        logging.info(f"找到 {len(txt_files)} 个txt文件")
        for file_path in txt_files:
            convert_file_encoding(str(file_path), target_encoding)
    except Exception as e:
        logging.error(f"批量转换过程中发生错误: {str(e)}")

if __name__ == '__main__':
    import argparse
    
    parser = argparse.ArgumentParser(description='批量转换txt文件的字符编码')
    parser.add_argument('directory', help='要处理的目录路径')
    parser.add_argument('target_encoding', help='目标编码（如：utf-8, gbk, gb2312等）')
    
    args = parser.parse_args()
    batch_convert_encoding(args.directory, args.target_encoding) 